package org.zoomdev.zoom.dao.entity;

import org.zoomdev.zoom.aop.Getter;
import org.zoomdev.zoom.aop.Setter;
import org.zoomdev.zoom.dao.SqlConfig;
import org.zoomdev.zoom.dao.adapters.ParameterAdapter;
import org.zoomdev.zoom.dao.adapters.ResultSetAdapter;
import org.zoomdev.zoom.dao.annotations.Column;
import org.zoomdev.zoom.dao.meta.ColumnTypeMeta;
import org.zoomdev.zoom.dao.meta.TableKeyMeta;
import org.zoomdev.zoom.dao.naming.NameMapping;
import org.zoomdev.zoom.dao.naming.NameMappingFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;

public final class Field2ColumnMappingConfig {

    public String updatePlaceholder;
    private String columnName;
    private Class<?> columnType;
    private int jdbcType;
    private String fieldName; //字段名称
    private Type fieldType; // 字段类型
    private String table;  //表
    private String alias; //表别名
    private Getter getter; //getter
    private ParameterAdapter parameterAdapter;
    private String insertPlaceholder;

    // 设置结果集
    private Setter setter;
    // 从resultset取值
    private ResultSetAdapter resultAdapter;

    public static Field2ColumnMappingConfig find(Field2ColumnMappingConfig[] columnMappingConfigs, Field field) {
        for (Field2ColumnMappingConfig config : columnMappingConfigs) {
            if (config.match(field)) {
                return config;
            }
        }
        return null;
    }

    public static Field2ColumnMappingConfig find(Field2ColumnMappingConfig[] columnMappingConfigs, String field) {
        for (Field2ColumnMappingConfig config : columnMappingConfigs) {
            if (config.match(field)) {
                return config;
            }
        }
        return null;
    }

    public static Field2ColumnMappingConfig[] fromSingle(NameMappingFactory factory,
                                                         TableSource tableSource,
                                                         TableKeyMeta table) {
        final NameMapping mapping = factory.create(Arrays.stream(table.getColumns())
                .map(ColumnTypeMeta::getName).toArray(String[]::new));
        return Arrays.stream(table.getColumns()).map(i -> {
            Field2ColumnMappingConfig config = new Field2ColumnMappingConfig();
            config.setColumnName(i.getName());
            config.setJdbcType(i.getJdbcType());
            config.setColumnType(i.getType());
            config.setTable(tableSource.getTable());
            config.setAlias(tableSource.getAlias());
            config.setFieldName(mapping.getMapping(i.getName()));
            return config;
        }).toArray(Field2ColumnMappingConfig[]::new);
    }

    public boolean match(Field field) {
        Column a = field.getAnnotation(Column.class);
        if (a != null) {
            String v = a.value();
            if (v.contains(".")) {
                String[] arr = v.split("\\.");
                String tblp = arr[0];       //有可能是一个别名
                String colp = arr[1];
                return tableMatch(tblp) && colp.equalsIgnoreCase(columnName);
            } else return v.equalsIgnoreCase(columnName);
        } else return field.getName().equals(fieldName);
    }

    public boolean match(String field) {
        if (field.contains(".")) {
            String[] arr = field.split("\\.");
            String tblp = arr[0];       //有可能是一个别名
            String colp = arr[1];
            return tableMatch(tblp) || colp.equalsIgnoreCase(fieldName);
        } else return field.equalsIgnoreCase(columnName) || field.equalsIgnoreCase(fieldName);
    }

    public String getColumnName() {
        return columnName;
    }

    public void setColumnName(String columnName) {
        this.columnName = columnName;
    }

    public Class<?> getColumnType() {
        return columnType;
    }

    public void setColumnType(Class<?> columnType) {
        this.columnType = columnType;
    }

    public int getJdbcType() {
        return jdbcType;
    }

    public void setJdbcType(int jdbcType) {
        this.jdbcType = jdbcType;
    }

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public Type getFieldType() {
        return fieldType;
    }

    public void setFieldType(Type fieldType) {
        this.fieldType = fieldType;
    }

    public String getTable() {
        return table;
    }

    public void setTable(String table) {
        this.table = table;
    }

    public String getAlias() {
        return alias;
    }

    public void setAlias(String alias) {
        this.alias = alias;
    }

    public Getter getGetter() {
        return getter;
    }

    public void setGetter(Getter getter) {
        this.getter = getter;
    }

    public ParameterAdapter getParameterAdapter() {
        return parameterAdapter;
    }

    public void setParameterAdapter(ParameterAdapter parameterAdapter) {
        this.parameterAdapter = parameterAdapter;
    }

    public String getInsertPlaceholder() {
        return insertPlaceholder;
    }

    public void setInsertPlaceholder(String insertPlaceholder) {
        this.insertPlaceholder = insertPlaceholder;
    }

    public Object get(Object data) {
        return getter.get(data);
    }

    public void setNonnullParameter(PreparedStatement stmt, int i, Object value) throws SQLException {
        parameterAdapter.setNonnullParameter(stmt, i, value);
    }

    public Setter getSetter() {
        return setter;
    }

    public void setSetter(Setter setter) {
        this.setter = setter;
    }

    public ResultSetAdapter getResultAdapter() {
        return resultAdapter;
    }

    public void setResultAdapter(ResultSetAdapter resultAdapter) {
        this.resultAdapter = resultAdapter;
    }

    public void set(Object data, Object value) {
        setter.set(data, value);
    }

    public void mapping(SqlConfig config, Object data, ResultSet rs, int index) throws SQLException {
        setter.set(data, resultAdapter.getObject(rs, index));
    }

    private boolean tableMatch(String tableName) {
        if (table.equalsIgnoreCase(tableName)) {
            return true;
        }
        if (alias != null) {
            return alias.equalsIgnoreCase(tableName);
        }
        return false;
    }
}
